﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Xml.Linq;
using Appology.SugarSync.Core.Security;
using Appology.SugarSync.IO;
using Appology.SugarSync.IO.Primitives;

namespace Appology.SugarSync
{
    public class SugarSyncApiSettings
    {
        public SugarSyncApiSettings()
        {
            Account = new SugarSyncAccount();
            StorageRoot = new SugarSyncStorageRoot("sugar sync account", Account);
            History = new Stack<SugarSyncPathInfo>();
        }

        public SugarSyncAccount Account { get; private set; }

        public SugarSyncStorageRoot StorageRoot { get; private set; }

        public string AccessKey { get; set; }
        public string PrivateAccessKey { get; set; }

        public string Username { get; set; }
        public string Password { get; set; }

        public Stack<SugarSyncPathInfo> History { get; set; }
        
        public static SugarSyncApiSettings Load(string isolatedStorageFilePath = "SugarSyncApiSettings.xml")
        {
            var sugarSyncApiSettings = new SugarSyncApiSettings();

            using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isolatedStorageFile.FileExists(isolatedStorageFilePath))
                {
                    using (var sr = new StreamReader(isolatedStorageFile.OpenFile(isolatedStorageFilePath, FileMode.Open)))
                    {
                        string base64EncodedEncryptedString = sr.ReadToEnd();
                        byte[] encryptedBytes = Convert.FromBase64String(base64EncodedEncryptedString);

                        string sugarSyncApiSettingsXml = Crypto.Decrypt(encryptedBytes);

                        XElement root = XElement.Parse(sugarSyncApiSettingsXml);
                        
                        sugarSyncApiSettings.AccessKey = root.Element("AccessKey").Value;
                        sugarSyncApiSettings.PrivateAccessKey = root.Element("PrivateAccessKey").Value;

                        sugarSyncApiSettings.Account.SetApiCredentials(sugarSyncApiSettings.AccessKey, sugarSyncApiSettings.PrivateAccessKey);

                        sugarSyncApiSettings.Username = root.Element("Username").Value;
                        sugarSyncApiSettings.Password = root.Element("Password").Value;

                        sugarSyncApiSettings.Account.SetUserCredentials(sugarSyncApiSettings.Username, sugarSyncApiSettings.Password);

                        XElement history = root.Element("History");

                        foreach (var element in history.Elements().Reverse())
                        {
                            if (element.Name.LocalName == "SugarSyncDirectoryInfo")
                            {
                                sugarSyncApiSettings.History.Push(GetDirectoryInfoFromXElement(sugarSyncApiSettings.Account, element));
                            }

                            if (element.Name.LocalName == "SugarSyncFileInfo")
                            {
                                sugarSyncApiSettings.History.Push(GetFileInfoFromXElement(sugarSyncApiSettings.Account, element));
                            }
                        }
                    }
                }
            }

            return sugarSyncApiSettings;
        }

        private static SugarSyncDirectoryInfo GetDirectoryInfoFromXElement(SugarSyncAccount account, XElement element)
        {
            string name = element.Element("Name").Value;

            Uri refUri = null;

            var refUriElement = element.Element("RefUri");
            if (refUriElement != null && !string.IsNullOrEmpty(refUriElement.Value))
            {
                refUri = new Uri(refUriElement.Value, UriKind.Absolute);
            }

            Uri contentsUri = null;

            var contentsUriElement = element.Element("ContentsUri");
            if (contentsUriElement != null && !string.IsNullOrEmpty(contentsUriElement.Value))
            {
                contentsUri = new Uri(contentsUriElement.Value, UriKind.Absolute);
            }

            return new SugarSyncDirectoryInfo(name, account, refUri, contentsUri);
        }

        private static SugarSyncFileInfo GetFileInfoFromXElement(SugarSyncAccount account, XElement element)
        {
            string name = element.Element("Name").Value;

            Uri refUri = null;

            var refUriElement = element.Element("RefUri");
            if (refUriElement != null && !string.IsNullOrEmpty(refUriElement.Value))
            {
                refUri = new Uri(refUriElement.Value, UriKind.Absolute);
            }

            return new SugarSyncFileInfo(name, account, refUri);
        }

        private XElement GetXElementFromDirectoryInfo(SugarSyncDirectoryInfo directoryInfo)
        {
            XElement element = XElement.Parse("<SugarSyncDirectoryInfo/>");

            XElement name = XElement.Parse("<Name/>");
            XElement refUri = XElement.Parse("<RefUri/>");
            XElement contentsUri = XElement.Parse("<ContentsUri/>");

            name.Value = directoryInfo.Name;

            if (directoryInfo.RefUri != null)
            {
                refUri.Value = directoryInfo.RefUri.ToString();
            }

            if (directoryInfo.ContentsUri != null)
            {
                contentsUri.Value = directoryInfo.ContentsUri.ToString();
            }

            element.Add(name);
            element.Add(refUri);
            element.Add(contentsUri);

            return element;
        }

        private XElement GetXElementFromFileInfo(SugarSyncFileInfo fileInfo)
        {
            XElement element = XElement.Parse("<SugarSyncFileInfo/>");

            XElement name = XElement.Parse("<Name/>");
            XElement refUri = XElement.Parse("<RefUri/>");

            name.Value = fileInfo.Name;

            if (fileInfo.RefUri != null)
            {
                refUri.Value = fileInfo.RefUri.ToString();
            }

            element.Add(name);
            element.Add(refUri);

            return element;
        }

        public void Save(string isolatedStorageFilePath = "SugarSyncApiSettings.xml")
        {
            XElement root = XElement.Parse("<SugarSyncApiSettings/>");

            XElement accessKey = XElement.Parse("<AccessKey/>");
            XElement privateAccessKey = XElement.Parse("<PrivateAccessKey/>");

            accessKey.Value = AccessKey;
            privateAccessKey.Value = PrivateAccessKey;

            root.Add(accessKey);
            root.Add(privateAccessKey);

            XElement username = XElement.Parse("<Username/>");
            XElement password = XElement.Parse("<Password/>");

            username.Value = Username;
            password.Value = Password;

            root.Add(username);
            root.Add(password);

            XElement history = XElement.Parse("<History/>");

            foreach (var pathInfo in History)
            {
                if (pathInfo is SugarSyncDirectoryInfo)
                {
                    history.Add(GetXElementFromDirectoryInfo((SugarSyncDirectoryInfo)pathInfo));
                }

                if (pathInfo is SugarSyncFileInfo)
                {
                    history.Add(GetXElementFromFileInfo((SugarSyncFileInfo)pathInfo));
                }
            }

            root.Add(history);

            byte[] encryptedBytes = Crypto.Encrypt(root.ToString());
            string base64EncodedEncryptedString = Convert.ToBase64String(encryptedBytes);

            using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isolatedStorageFile.FileExists(isolatedStorageFilePath))
                {
                    isolatedStorageFile.DeleteFile(isolatedStorageFilePath);
                }

                using (var sw = new StreamWriter(isolatedStorageFile.OpenFile(isolatedStorageFilePath, FileMode.Create)))
                {
                    sw.Write(base64EncodedEncryptedString);
                    sw.Flush();
                }
            }
        }
    }
}
